package testview.zhen.com.myapplication.asm_demo

import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.ClassNode
import testview.zhen.com.myapplication.hencoder.practice_draw1.PageFragment.newInstance
import java.io.FileOutputStream
import java.nio.charset.StandardCharsets

/**
 * @author :create by ldr
 * time:2022/10/27 15:04
 * @descript:
 */
fun main() {
    TestASM.addCodePrint()
}


object TestASM {

    /**
     * ASM读取类文件
     */
    fun readClassPrinterVisitor(){
        val classReader = ClassReader("testview.zhen.com.myapplication.asm_demo.User")
        val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
        val visitorPrinter = ClassPrinterVisitor(classWriter)
        classReader.accept(visitorPrinter, ClassReader.EXPAND_FRAMES)
    }

    fun readClassNodePrint(){
        val classReader = ClassReader("testview.zhen.com.myapplication.asm_demo.User")
        println("---------ClassNode-----")
        val classNode = ClassNode(Opcodes.ASM9)
        classReader.accept(classNode,0)

        val methodsList = classNode.methods
        val fieldsList = classNode.fields
        println("methods")
        for (methodNode in methodsList) {
            println("${methodNode.name} ${methodNode.desc}")
        }
        println("fieldNode")
        fieldsList.forEach {
            println("${it.name} ${it.desc}")
        }
    }


    fun addCodePrint(){
        val classReader = ClassReader("testview.zhen.com.myapplication.asm_demo.User")

        val classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS)

        val addCodeClassVisitor = AddCodeClassVisitor(classWriter)
        classReader.accept(addCodeClassVisitor,0)
        val byteArraylist = classWriter.toByteArray()
        //反射调用 看看是否有插入成功
        val loader = MyClassLoader()
        val class1 = loader.defineClass(null,byteArraylist)
        val method = class1?.getDeclaredMethod("toString")
        method?.invoke(class1.newInstance())
        //打印文字
        println("${String(byteArraylist,StandardCharsets.UTF_8)}")
        //写入文件
        val fos = FileOutputStream("./app/src/main/java/testview/zhen/com/myapplication/asm_demo/User2.class")
        fos.write(byteArraylist)
        fos.close()
    }

    fun newClassAndShow(): Unit {
        //定义一个叫做Example的类
        val cw = ClassWriter(0);
        cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Example", null, "java/lang/Object", null);

        //生成默认的构造方法
        var mw = cw.visitMethod(Opcodes.ACC_PUBLIC,
        "<init>",
        "()V",
        null,
        null);

        //生成构造方法的字节码指令
        mw.visitVarInsn(Opcodes.ALOAD, 0);
        mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
        mw.visitInsn(Opcodes.RETURN);
        mw.visitMaxs(1, 1);
        mw.visitEnd();

        //生成main方法
        mw = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC,
            "main",
            "([Ljava/lang/String;)V",
            null,
            null);

        //生成main方法中的字节码指令
        mw.visitFieldInsn(Opcodes.GETSTATIC,
            "java/lang/System",
            "out",
            "Ljava/io/PrintStream;");

        mw.visitLdcInsn("Hello world!");
        mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
            "java/io/PrintStream",
            "println",
            "(Ljava/lang/String;)V");
        mw.visitInsn(Opcodes.RETURN);
        mw.visitMaxs(2, 2);

        //字节码生成完成
        mw.visitEnd();

        // 获取生成的class文件对应的二进制流
        var code = cw.toByteArray();


        //将二进制流写到本地磁盘上
        val fos =  FileOutputStream("./app/src/main/java/testview/zhen/com/myapplication/asm_demo/Example.class");
        fos.write(code);
        fos.close();


//        //直接将二进制流加载到内存中
//        val loader =  Helloworld()
//        val exampleClass = loader.defineClass("Example", code, 0, code.length);
//
//        //通过反射调用main方法
//        exampleClass.getMethods()[0].invoke(null, arrayOf(null));

    }

}